/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.openbookformat.project.newprojectwizard;

import com.smallutil.io.FileRW;
import com.smallutil.io.WebUtil;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import javax.swing.JComponent;
import org.htmlparser.util.ParserException;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.ui.OpenProjects;
import org.openbookformat.project.Chapter;
import org.openbookformat.project.HeadingPointer;
import org.openbookformat.tags.PagebreakTag;
import org.openbookformat.tags.WarningTag;
import org.openide.DialogDisplayer;
import org.openide.WizardDescriptor;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.execution.ExecutionEngine;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.openide.windows.IOProvider;
import org.openide.windows.InputOutput;

// An example action demonstrating how the wizard could be called from within
// your code. You can move the code below wherever you need, or register an action:
// @ActionID(category="...", id="org.openbookformat.project.newprojectwizard.NewOBFProjectWizardAction")
// @ActionRegistration(displayName="Open NewOBFProject Wizard")
// @ActionReference(path="Menu/Tools", position=...)
@ActionID(category = "File",
id="org.openbookformat.project.newprojectwizard.NewOBFProjectWizardAction")
@ActionRegistration(iconBase = "org/openbookformat/project/newprojectwizard/book_open.png",
displayName = "New Project from Existing Code")
@ActionReferences({
    @ActionReference(path = "Menu/File", position = 150),
    @ActionReference(path = "Shortcuts", name = "DO-N"),
    @ActionReference(path = "Shortcuts", name = "D-N")
})
public final class NewOBFProjectWizardAction implements ActionListener {

    private WizardDescriptor.Panel[] panels;

    public @Override
    void actionPerformed(ActionEvent e) {
        WizardDescriptor wizardDescriptor = new WizardDescriptor(getPanels());
        // {0} will be replaced by WizardDesriptor.Panel.getComponent().getName()
        wizardDescriptor.setTitleFormat(new MessageFormat("{0}"));
        wizardDescriptor.setTitle("Create new OBF project");
        Dialog dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor);
        dialog.setVisible(true);
        dialog.toFront();
        boolean cancelled = wizardDescriptor.getValue() != WizardDescriptor.FINISH_OPTION;
        if (!cancelled) {
            createProject(wizardDescriptor);
        }
    }

    /**
     * Initialize panels representing individual wizard's steps and sets
     * various properties for them influencing wizard appearance.
     */
    private WizardDescriptor.Panel[] getPanels() {
        if (panels == null) {
            panels = new WizardDescriptor.Panel[]{
                new NewOBFProjectWizardPanel1(),
                new NewOBFProjectWizardPanel2(),
                new NewOBFProjectWizardPanel3()//,
                //new NewOBFProjectWizardPanel4()
            };
            String[] steps = new String[panels.length];
            for (int i = 0; i < panels.length; i++) {
                Component c = panels[i].getComponent();
                // Default step name to component name of panel. Mainly useful
                // for getting the name of the target chooser to appear in the
                // list of steps.
                steps[i] = c.getName();
                if (c instanceof JComponent) { // assume Swing components
                    JComponent jc = (JComponent) c;
                    // Sets step number of a component
                    // TODO if using org.openide.dialogs >= 7.8, can use WizardDescriptor.PROP_*:
                    jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i));
                    // Sets steps names for a panel
                    jc.putClientProperty("WizardPanel_contentData", steps);
                    // Turn on subtitle creation on each step
                    jc.putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE);
                    // Show steps on the left side with the image on the background
                    jc.putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE);
                    // Turn on numbering of all steps
                    jc.putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE);
                }
            }
        }
        return panels;
    }

    private boolean createProject(final WizardDescriptor wd) {
        ExecutionEngine engine = ExecutionEngine.getDefault();
        engine.execute("Creating OBF Project ...", new Runnable() {

            @Override
            public void run() {
                String NL = "\n";
                InputOutput inOut = IOProvider.getDefault().getIO("Creating OBF Project ...", null);
                ProgressHandle handle = ProgressHandleFactory.createHandle(
                        "Creating OBF Project ...");
                boolean success = false;
                handle.start();

                //creating project directory
                String projDir = (String) wd.getProperty(NewOBFProjectVisualPanel1.PROP_PROJECTFOLDER);
                inOut.getOut().print("- Creating project directory... ");
                handle.progress("Creating project directory...");
                success = (new File(projDir)).mkdirs();
                inOut.getOut().println(success ? "done." : "fail.");
                inOut.getOut().print("- Creating project source directory... ");
                handle.progress("Creating project source directory..");
                success = (new File(projDir + File.separator + "src")).mkdirs();
                inOut.getOut().println(success ? "done." : "fail.");
                if(!success) {
                    inOut.getOut().println("Could not create project directory " + projDir + ".");
                    handle.finish();
                    return;
                }

                //processing files
                StringBuilder buff = new StringBuilder();
                ArrayList<HeadingPointer> pointers = new ArrayList<HeadingPointer>();
                ArrayList<String> subpages = (ArrayList<String>) wd.getProperty(
                        NewOBFProjectVisualPanel2.PROP_SUBPAGES);
                String encoding = (String) wd.getProperty(
                        NewOBFProjectWizardPanel2.PROP_ENCODING);

                String pagePattern = (String) wd.getProperty(
                        NewOBFProjectVisualPanel3.PROP_PAGE_PATTERN);
                boolean userPagePattern = (Boolean) wd.getProperty(
                        NewOBFProjectVisualPanel3.PROP_USE_PAGE_GROUP);
                int pageGroup = (Integer) wd.getProperty(
                        NewOBFProjectVisualPanel3.PROP_PAGE_GROUP);

                String headingsPattern = (String) wd.getProperty(
                        NewOBFProjectVisualPanel3.PROP_HEADINGS_PATTERN);
                boolean userheadingsPattern = (Boolean) wd.getProperty(
                        NewOBFProjectVisualPanel3.PROP_USE_HEADINGS_GROUP);
                int headingsGroup = (Integer) wd.getProperty(
                        NewOBFProjectVisualPanel3.PROP_HEADINGS_GROUP);
                boolean userAutoLevel = (Boolean) wd.getProperty(
                        NewOBFProjectVisualPanel3.PROP_USE_AUTOLEVEL);
                int levelGroup = (Integer) wd.getProperty(
                        NewOBFProjectVisualPanel3.PROP_LEVEL_GROUP);
                if(!userAutoLevel)
                    levelGroup = -1;
                
                int numOfChpt = (Integer) wd.getProperty(
                        NewOBFProjectVisualPanel2.PROP_NUMBEROFCHAPTERS);
                if (numOfChpt > subpages.size()) {
                    numOfChpt = subpages.size();
                }
                inOut.getOut().println("- Processing input files...");
                inOut.getOut().println("  + Compiling " + subpages.size()
                        + " file[s] into " + numOfChpt + " chapters...");
                handle.progress("Processing input files...");
                int spIdx = 0;
                int spCount = 0;
                String contents = null;
                String chptPath = null;
                int curFirstPage = -1;
                int curLastPage = -1;
                int nextFirstPage = -1;
                int nextLastPage = -1;
                int[] r = {-1, -1};
                for (int i = 0; i < numOfChpt; i++) {
                    try {
                        spCount = subpages.size() / numOfChpt
                                + ((i + 1) <= subpages.size() % numOfChpt ? 1 : 0);
                        inOut.getOut().println("  + Compiling[" + (i + 1) + "/"
                                + numOfChpt + "]: Chapter " + (i + 1) + " - "
                                + spCount + " subpage[s].");
                        handle.progress("  + Compiling[" + (i + 1) + "/"
                                + numOfChpt + "]: Chapter " + (i + 1) + " - "
                                + spCount + " subpage[s].");

                        buff.setLength(0);
                        buff.append("<?xml version=\"1.0\" encoding=\"").
                                append(encoding).append("\"?>").append(NL);
                        buff.append("<book-part index='").append(i + 1).append("'>").append(NL);

                        for (int j = 0; j < spCount; j++) {
                            try {
                                inOut.getOut().println("    * Processing[" + (spIdx + 1) + "/"
                                        + subpages.size() + "]:" + subpages.get(spIdx));
                                //processing each link
                                contents = WebUtil.getHttp(subpages.get(spIdx), encoding);

                                if (spIdx + 1 < subpages.size()) {

                                    //find first and last page [current link]
                                    if (nextFirstPage == -1) {
                                        r = NewOBFProjectUtil.getFirstnLastPage(
                                                subpages.get(spIdx), encoding,
                                                pagePattern, userPagePattern, pageGroup);
                                        curFirstPage = r[0];
                                        curLastPage = r[1];
                                    } else {
                                        curFirstPage = nextFirstPage;
                                        curLastPage = nextLastPage;
                                    }

                                    //find first and last page [next link]
                                    r = NewOBFProjectUtil.getFirstnLastPage(
                                            subpages.get(spIdx + 1), encoding,
                                            pagePattern, userPagePattern, pageGroup);
                                    nextFirstPage = r[0];
                                    nextLastPage = r[1];
                                    if (nextFirstPage == -1) {
                                        inOut.getErr().println("      - Redetecting "
                                                + "page break pattern for next file.");
                                        r = NewOBFProjectUtil.getFirstnLastPage(
                                                subpages.get(spIdx + 1), encoding,
                                                NewOBFProjectUtil.getPagePattern(
                                                NewOBFProjectUtil.html2Text(
                                                WebUtil.getHttp(subpages.get(
                                                spIdx + 1), encoding))),
                                                userPagePattern, pageGroup);
                                        nextFirstPage = r[0];
                                        nextLastPage = r[1];
                                    }
                                }
                                inOut.getOut().println("      - First Page --> " + curFirstPage
                                        + ", Last Page --> " + curLastPage);

                                buff.append(NewOBFProjectUtil.html2Text(contents));
                                if (spIdx + 1 < subpages.size()
                                        && nextFirstPage - curLastPage > 1) {
                                    buff.append("\n" + (new PagebreakTag(
                                            curLastPage + 1)).toString());
                                }

                                if (spIdx + 1 < subpages.size()
                                        && curLastPage - curFirstPage < 0) {
                                    buff.append("\n" + (new WarningTag(""
                                            + "Check page numbers")).toString());
                                }

                                //processing headings
                                pointers.addAll(NewOBFProjectUtil.getHeadings(contents,
                                        new Chapter("Chapter" + ((i + 1) < 9 ? "0" : "") + (i + 1)),
                                        headingsPattern, levelGroup, headingsGroup, 1));

                                spIdx++;
                            } catch (IOException ex) {
                                Exceptions.printStackTrace(ex);
                            } catch (ParserException ex) {
                                Exceptions.printStackTrace(ex);
                            }
                        }
                        buff.append("</book-part>");

                        chptPath = projDir + File.separator + "src"
                                + File.separator + "Chapter" + ((i + 1) < 9 ? "0" : "")
                                + (i + 1) + ".obf";
                        inOut.getOut().println("  + Saving " + chptPath + " ...");
                        FileRW.save(chptPath, buff.toString(), encoding);
                    } catch (IOException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                }


                //obfproject.xml 
                inOut.getOut().print("- Creating project file obfproject.xml...");
                handle.progress("Creating project file obfproject.xml...");
                buff.setLength(0);
                buff.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>").append(NL);
                buff.append("<project-prop>").append(NL);
                buff.append("\t<chapters>").append(NL);
                for (int i = 0; i < numOfChpt; i++) {
                    buff.append("\t\t<chapter>Chapter").append((i + 1) < 9 ? "0" : "").append(i + 1).append("</chapter>").append(NL);
                }
                buff.append("\t</chapters>").append(NL);
                buff.append("</project-prop>").append(NL);
                try {
                    FileRW.save(projDir + File.separator + "obfproject.xml",
                            buff.toString(), "utf-8");
                } catch (IOException ex) {
                    Exceptions.printStackTrace(ex);
                }
                inOut.getOut().println(" done.");


                //headings.properties
                inOut.getOut().print("- Creating headings file headings.properties ...");
                handle.progress("Creating headings file headings.properties");
                SortedProperties headings = new SortedProperties();
                //appending headings
                String ptrIdx = null;
                String heading = null;
                for (int i = 0; i < pointers.size(); i++) {
                    heading = NewOBFProjectUtil.html2Text(pointers.get(i).getHeading());
                    if (heading.trim().length() == 0) {
                        pointers.remove(i);
                        i--;
                        continue;
                    }
                    //ptrIdx = ((i + 1) < 99 ? "0" : "") + ((i + 1) < 9 ? "0" : "") + i;
                    ptrIdx = (i < 100?"0":"") + (i < 10?"0":"") + i;
                    headings.put("idx_" + ptrIdx + "_chapter",
                            pointers.get(i).getChapter().getTitle());
                    headings.put("idx_" + ptrIdx + "_offset",
                            pointers.get(i).getOffset() + "");
                    headings.put("idx_" + ptrIdx + "_level",
                            pointers.get(i).getLevel() + "");
                    headings.put("idx_" + ptrIdx + "_heading",
                            heading);
                }
                headings.put("heading_count", pointers.size() + "");
                try {
                    headings.store(new FileOutputStream(
                            projDir + File.separator + "headings.properties"), null);
                } catch (IOException ex) {
                    Exceptions.printStackTrace(ex);
                }
                inOut.getOut().println(" done.");
                try {
                    OpenProjects.getDefault().open(new Project[]{
                        ProjectManager.getDefault().findProject(
                            FileUtil.toFileObject(new File(projDir)))}, false);
                } catch (IOException ex) {
                    Exceptions.printStackTrace(ex);
                } catch (IllegalArgumentException ex) {
                    Exceptions.printStackTrace(ex);
                }
                handle.finish();
                
            }
        }, InputOutput.NULL);
        
        return true;
    }

}
